home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / unix / tkUnixButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  14.0 KB  |  479 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkUnixButton.c --
  3.  *
  4.  *    This file implements the Unix specific portion of the button
  5.  *    widgets.
  6.  *
  7.  * Copyright (c) 1996 by Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkUnixButton.c 1.4 97/06/06 11:21:40
  13.  */
  14.  
  15. #include "tkButton.h"
  16.  
  17. /*
  18.  * Declaration of Unix specific button structure.
  19.  */
  20.  
  21. typedef struct UnixButton {
  22.     TkButton info;        /* Generic button info. */
  23. } UnixButton;
  24.  
  25. /*
  26.  * The class procedure table for the button widgets.
  27.  */
  28.  
  29. TkClassProcs tkpButtonProcs = { 
  30.     NULL,            /* createProc. */
  31.     TkButtonWorldChanged,    /* geometryProc. */
  32.     NULL            /* modalProc. */
  33. };
  34.  
  35. /*
  36.  *----------------------------------------------------------------------
  37.  *
  38.  * TkpCreateButton --
  39.  *
  40.  *    Allocate a new TkButton structure.
  41.  *
  42.  * Results:
  43.  *    Returns a newly allocated TkButton structure.
  44.  *
  45.  * Side effects:
  46.  *    Registers an event handler for the widget.
  47.  *
  48.  *----------------------------------------------------------------------
  49.  */
  50.  
  51. TkButton *
  52. TkpCreateButton(tkwin)
  53.     Tk_Window tkwin;
  54. {
  55.     UnixButton *butPtr = (UnixButton *)ckalloc(sizeof(UnixButton));
  56.     return (TkButton *) butPtr;
  57. }
  58.  
  59. /*
  60.  *----------------------------------------------------------------------
  61.  *
  62.  * TkpDisplayButton --
  63.  *
  64.  *    This procedure is invoked to display a button widget.  It is
  65.  *    normally invoked as an idle handler.
  66.  *
  67.  * Results:
  68.  *    None.
  69.  *
  70.  * Side effects:
  71.  *    Commands are output to X to display the button in its
  72.  *    current mode.  The REDRAW_PENDING flag is cleared.
  73.  *
  74.  *----------------------------------------------------------------------
  75.  */
  76.  
  77. void
  78. TkpDisplayButton(clientData)
  79.     ClientData clientData;    /* Information about widget. */
  80. {
  81.     register TkButton *butPtr = (TkButton *) clientData;
  82.     GC gc;
  83.     Tk_3DBorder border;
  84.     Pixmap pixmap;
  85.     int x = 0;            /* Initialization only needed to stop
  86.                  * compiler warning. */
  87.     int y, relief;
  88.     register Tk_Window tkwin = butPtr->tkwin;
  89.     int width, height;
  90.     int offset;            /* 0 means this is a label widget.  1 means
  91.                  * it is a flavor of button, so we offset
  92.                  * the text to make the button appear to
  93.                  * move up and down as the relief changes. */
  94.  
  95.     butPtr->flags &= ~REDRAW_PENDING;
  96.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  97.     return;
  98.     }
  99.  
  100.     border = butPtr->normalBorder;
  101.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  102.     gc = butPtr->disabledGC;
  103.     } else if ((butPtr->state == tkActiveUid)
  104.         && !Tk_StrictMotif(butPtr->tkwin)) {
  105.     gc = butPtr->activeTextGC;
  106.     border = butPtr->activeBorder;
  107.     } else {
  108.     gc = butPtr->normalTextGC;
  109.     }
  110.     if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
  111.         && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  112.     border = butPtr->selectBorder;
  113.     }
  114.  
  115.     /*
  116.      * Override the relief specified for the button if this is a
  117.      * checkbutton or radiobutton and there's no indicator.
  118.      */
  119.  
  120.     relief = butPtr->relief;
  121.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  122.     relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
  123.         : TK_RELIEF_RAISED;
  124.     }
  125.  
  126.     offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);
  127.  
  128.     /*
  129.      * In order to avoid screen flashes, this procedure redraws
  130.      * the button in a pixmap, then copies the pixmap to the
  131.      * screen in a single operation.  This means that there's no
  132.      * point in time where the on-sreen image has been cleared.
  133.      */
  134.  
  135.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  136.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  137.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  138.         Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  139.  
  140.     /*
  141.      * Display image or bitmap or text for button.
  142.      */
  143.  
  144.     if (butPtr->image != None) {
  145.     Tk_SizeOfImage(butPtr->image, &width, &height);
  146.  
  147.     imageOrBitmap:
  148.     TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
  149.         butPtr->indicatorSpace + width, height, &x, &y);
  150.     x += butPtr->indicatorSpace;
  151.  
  152.     x += offset;
  153.     y += offset;
  154.     if (relief == TK_RELIEF_RAISED) {
  155.         x -= offset;
  156.         y -= offset;
  157.     } else if (relief == TK_RELIEF_SUNKEN) {
  158.         x += offset;
  159.         y += offset;
  160.     }
  161.     if (butPtr->image != NULL) {
  162.         if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  163.         Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap,
  164.             x, y);
  165.         } else {
  166.         Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
  167.             x, y);
  168.         }
  169.     } else {
  170.         XSetClipOrigin(butPtr->display, gc, x, y);
  171.         XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  172.             (unsigned int) width, (unsigned int) height, x, y, 1);
  173.         XSetClipOrigin(butPtr->display, gc, 0, 0);
  174.     }
  175.     y += height/2;
  176.     } else if (butPtr->bitmap != None) {
  177.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  178.     goto imageOrBitmap;
  179.     } else {
  180.     TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
  181.         butPtr->indicatorSpace + butPtr->textWidth, butPtr->textHeight,
  182.         &x, &y);
  183.  
  184.     x += butPtr->indicatorSpace;
  185.  
  186.     x += offset;
  187.     y += offset;
  188.     if (relief == TK_RELIEF_RAISED) {
  189.         x -= offset;
  190.         y -= offset;
  191.     } else if (relief == TK_RELIEF_SUNKEN) {
  192.         x += offset;
  193.         y += offset;
  194.     }
  195.     Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
  196.         x, y, 0, -1);
  197.     Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
  198.         butPtr->textLayout, x, y, butPtr->underline);
  199.     y += butPtr->textHeight/2;
  200.     }
  201.  
  202.     /*
  203.      * Draw the indicator for check buttons and radio buttons.  At this
  204.      * point x and y refer to the top-left corner of the text or image
  205.      * or bitmap.
  206.      */
  207.  
  208.     if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  209.     int dim;
  210.  
  211.     dim = butPtr->indicatorDiameter;
  212.     x -= butPtr->indicatorSpace;
  213.     y -= dim/2;
  214.     if (dim > 2*butPtr->borderWidth) {
  215.         Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
  216.             butPtr->borderWidth, 
  217.             (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
  218.             TK_RELIEF_RAISED);
  219.         x += butPtr->borderWidth;
  220.         y += butPtr->borderWidth;
  221.         dim -= 2*butPtr->borderWidth;
  222.         if (butPtr->flags & SELECTED) {
  223.         GC gc;
  224.  
  225.         gc = Tk_3DBorderGC(tkwin,(butPtr->selectBorder != NULL)
  226.             ? butPtr->selectBorder : butPtr->normalBorder,
  227.             TK_3D_FLAT_GC);
  228.         XFillRectangle(butPtr->display, pixmap, gc, x, y,
  229.             (unsigned int) dim, (unsigned int) dim);
  230.         } else {
  231.         Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
  232.             dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
  233.         }
  234.     }
  235.     } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
  236.     XPoint points[4];
  237.     int radius;
  238.  
  239.     radius = butPtr->indicatorDiameter/2;
  240.     points[0].x = x - butPtr->indicatorSpace;
  241.     points[0].y = y;
  242.     points[1].x = points[0].x + radius;
  243.     points[1].y = points[0].y + radius;
  244.     points[2].x = points[1].x + radius;
  245.     points[2].y = points[0].y;
  246.     points[3].x = points[1].x;
  247.     points[3].y = points[0].y - radius;
  248.     if (butPtr->flags & SELECTED) {
  249.         GC gc;
  250.  
  251.         gc = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
  252.             ? butPtr->selectBorder : butPtr->normalBorder,
  253.             TK_3D_FLAT_GC);
  254.         XFillPolygon(butPtr->display, pixmap, gc, points, 4, Convex,
  255.             CoordModeOrigin);
  256.     } else {
  257.         Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
  258.             4, butPtr->borderWidth, TK_RELIEF_FLAT);
  259.     }
  260.     Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth,
  261.         (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
  262.         TK_RELIEF_RAISED);
  263.     }
  264.  
  265.     /*
  266.      * If the button is disabled with a stipple rather than a special
  267.      * foreground color, generate the stippled effect.  If the widget
  268.      * is selected and we use a different background color when selected,
  269.      * must temporarily modify the GC.
  270.      */
  271.  
  272.     if ((butPtr->state == tkDisabledUid)
  273.         && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  274.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  275.         && (butPtr->selectBorder != NULL)) {
  276.         XSetForeground(butPtr->display, butPtr->disabledGC,
  277.             Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  278.     }
  279.     XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
  280.         butPtr->inset, butPtr->inset,
  281.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
  282.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
  283.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  284.         && (butPtr->selectBorder != NULL)) {
  285.         XSetForeground(butPtr->display, butPtr->disabledGC,
  286.             Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  287.     }
  288.     }
  289.  
  290.     /*
  291.      * Draw the border and traversal highlight last.  This way, if the
  292.      * button's contents overflow they'll be covered up by the border.
  293.      * This code is complicated by the possible combinations of focus
  294.      * highlight and default rings.  We draw the focus and highlight rings
  295.      * using the highlight border and highlight foreground color.
  296.      */
  297.  
  298.     if (relief != TK_RELIEF_FLAT) {
  299.     int inset = butPtr->highlightWidth;
  300.     if (butPtr->defaultState == tkActiveUid) {
  301.         /*
  302.          * Draw the default ring with 2 pixels of space between the
  303.          * default ring and the button and the default ring and the
  304.          * focus ring.  Note that we need to explicitly draw the space
  305.          * in the highlightBorder color to ensure that we overwrite any
  306.          * overflow text and/or a different button background color.
  307.          */
  308.  
  309.         Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
  310.             inset, Tk_Width(tkwin) - 2*inset,
  311.             Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
  312.         inset += 2;
  313.         Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
  314.             inset, Tk_Width(tkwin) - 2*inset,
  315.             Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN);
  316.         inset++;
  317.         Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
  318.             inset, Tk_Width(tkwin) - 2*inset,
  319.             Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
  320.  
  321.         inset += 2;
  322.     } else if (butPtr->defaultState == tkNormalUid) {
  323.         /*
  324.          * Leave room for the default ring and write over any text or
  325.          * background color.
  326.          */
  327.  
  328.         Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0,
  329.             0, Tk_Width(tkwin),
  330.             Tk_Height(tkwin), 5, TK_RELIEF_FLAT);
  331.         inset += 5;
  332.     }
  333.  
  334.     /*
  335.      * Draw the button border.
  336.      */
  337.  
  338.     Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
  339.         Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
  340.         butPtr->borderWidth, relief);
  341.     }
  342.     if (butPtr->highlightWidth != 0) {
  343.     GC gc;
  344.  
  345.     if (butPtr->flags & GOT_FOCUS) {
  346.         gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
  347.     } else {
  348.         gc = Tk_GCForColor(Tk_3DBorderColor(butPtr->highlightBorder),
  349.             pixmap);
  350.     }
  351.  
  352.     /*
  353.      * Make sure the focus ring shrink-wraps the actual button, not the
  354.      * padding space left for a default ring.
  355.      */
  356.  
  357.     if (butPtr->defaultState == tkNormalUid) {
  358.         TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth,
  359.             pixmap, 5);
  360.     } else {
  361.         Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
  362.     }
  363.     }
  364.  
  365.     /*
  366.      * Copy the information from the off-screen pixmap onto the screen,
  367.      * then delete the pixmap.
  368.      */
  369.  
  370.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  371.         butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  372.         (unsigned) Tk_Height(tkwin), 0, 0);
  373.     Tk_FreePixmap(butPtr->display, pixmap);
  374. }
  375.  
  376. /*
  377.  *----------------------------------------------------------------------
  378.  *
  379.  * TkpComputeButtonGeometry --
  380.  *
  381.  *    After changes in a button's text or bitmap, this procedure
  382.  *    recomputes the button's geometry and passes this information
  383.  *    along to the geometry manager for the window.
  384.  *
  385.  * Results:
  386.  *    None.
  387.  *
  388.  * Side effects:
  389.  *    The button's window may change size.
  390.  *
  391.  *----------------------------------------------------------------------
  392.  */
  393.  
  394. void
  395. TkpComputeButtonGeometry(butPtr)
  396.     register TkButton *butPtr;    /* Button whose geometry may have changed. */
  397. {
  398.     int width, height, avgWidth;
  399.     Tk_FontMetrics fm;
  400.  
  401.     if (butPtr->highlightWidth < 0) {
  402.     butPtr->highlightWidth = 0;
  403.     }
  404.     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
  405.  
  406.     /*
  407.      * Leave room for the default ring if needed.
  408.      */
  409.  
  410.     if (butPtr->defaultState != tkDisabledUid) {
  411.     butPtr->inset += 5;
  412.     }
  413.     butPtr->indicatorSpace = 0;
  414.     if (butPtr->image != NULL) {
  415.     Tk_SizeOfImage(butPtr->image, &width, &height);
  416.     imageOrBitmap:
  417.     if (butPtr->width > 0) {
  418.         width = butPtr->width;
  419.     }
  420.     if (butPtr->height > 0) {
  421.         height = butPtr->height;
  422.     }
  423.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  424.         butPtr->indicatorSpace = height;
  425.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  426.         butPtr->indicatorDiameter = (65*height)/100;
  427.         } else {
  428.         butPtr->indicatorDiameter = (75*height)/100;
  429.         }
  430.     }
  431.     } else if (butPtr->bitmap != None) {
  432.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  433.     goto imageOrBitmap;
  434.     } else {
  435.     Tk_FreeTextLayout(butPtr->textLayout);
  436.     butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
  437.         butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0,
  438.         &butPtr->textWidth, &butPtr->textHeight);
  439.  
  440.     width = butPtr->textWidth;
  441.     height = butPtr->textHeight;
  442.     avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
  443.     Tk_GetFontMetrics(butPtr->tkfont, &fm);
  444.  
  445.     if (butPtr->width > 0) {
  446.         width = butPtr->width * avgWidth;
  447.     }
  448.     if (butPtr->height > 0) {
  449.         height = butPtr->height * fm.linespace;
  450.     }
  451.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  452.         butPtr->indicatorDiameter = fm.linespace;
  453.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  454.         butPtr->indicatorDiameter = (80*butPtr->indicatorDiameter)/100;
  455.         }
  456.         butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
  457.     }
  458.     }
  459.  
  460.     /*
  461.      * When issuing the geometry request, add extra space for the indicator,
  462.      * if any, and for the border and padding, plus two extra pixels so the
  463.      * display can be offset by 1 pixel in either direction for the raised
  464.      * or lowered effect.
  465.      */
  466.  
  467.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
  468.     width += 2*butPtr->padX;
  469.     height += 2*butPtr->padY;
  470.     }
  471.     if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
  472.     width += 2;
  473.     height += 2;
  474.     }
  475.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
  476.         + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
  477.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  478. }
  479.